'''
Created on Jun 21, 2012

@author: thomas_dao
'''
from google.appengine.ext import ndb
from entity.entity import Bill, FriendBill, Friend
from web import util
from unique import unique
import logging

@ndb.tasklet
def create_friends(input_friends):
    # Use projection query to maximize performance
    proj = util.ndb_projection(cls=Friend, exclude=['friend_of'])
    qry = Friend.query(Friend.friend_of == util.get_current_user_key())
    friend_list = yield qry.fetch_async(projection=proj)
    entities = []
    existing_emails = [x.email for x in friend_list]
    
    for i_friend in input_friends:
        if i_friend.get('email') not in existing_emails:
            new_friend = Friend()
            new_friend.email = i_friend.get('email')
            new_friend.name = i_friend.get('name')
            new_friend.friend_of = util.get_current_user_key()
            entities.append(new_friend)
            
    if len(entities) > 0:
        yield ndb.put_multi_async(entities)
        friend_list = yield qry.fetch_async(projection=proj)
    
    raise ndb.Return(friend_list)

@ndb.tasklet
def create_friend_bills(friend_bills=None, bill=None):
    input_friends = []
    for friend_bill in friend_bills:
        i_friend = {}
        i_friend['email'] = friend_bill.get('email')
        i_friend['name'] = friend_bill.get('name')
        input_friends.append(i_friend)
        
    friend_list = yield create_friends(input_friends)
    
    entities = []
    
    for friend_bill in friend_bills:
        friend = [x for x in friend_list 
                  if x.email == friend_bill.get('email')]
        friend = friend[0]
        
        # Create bill for each friend
        new_friend_bill = FriendBill()
        new_friend_bill.friend = friend.key
        new_friend_bill.bill = bill.key
        new_friend_bill.owner = bill.owner
        new_friend_bill.amount = float(friend_bill.get('amount'))
        paid = friend_bill.get('paid')
        if paid and paid == "paid":
            new_friend_bill.paid = True
        entities.append(new_friend_bill)
        
    yield ndb.put_multi_async(entities)
    
    raise ndb.Return()

@ndb.tasklet
def bill_page(bills, friends, friend_bills):
    page = []    

    # Retrieve bill shared to friends
    for bill in bills:
        # bill is paid in full until we find one bill not yet paid
        paid = True
        paid_amount = 0
        unpaid_amount = 0
        
        # Get dict representation of bill, then construct the original
        # data
        bill_dict = util.ndb_to_dict(bill)
        
        # Get FriendBill attached to current bill
        filter_friend_bills = [x for x in friend_bills if x.bill==bill.key]
        friend_bills_list = []
        for friend_bill in filter_friend_bills:
            # Retrieve friend info
            friend_bill_dict = util.ndb_to_dict(friend_bill)
            
            # Find friend 
            friend = [x for x in friends if x.key == friend_bill.friend]
            friend = friend[0]
            
            friend_bill_dict['friend'] = util.ndb_to_dict(friend)
            if not friend_bill.paid:
                unpaid_amount += friend_bill.amount
                paid = False
            else: 
                paid_amount += friend_bill.amount
            friend_bills_list.append(friend_bill_dict)
        
        bill_dict['friend_bills'] = friend_bills_list
        
        # Separate into paid and unpaid section
        bill_dict['unpaid_amount'] = unpaid_amount
        bill_dict['paid_amount'] = paid_amount
        bill_dict['paid'] = paid
        page.append(bill_dict)
                    
    # Return results
    raise ndb.Return(page)

@ndb.tasklet
def friends_async(user_key):
    """
    Get friends list using projection query
    """
    proj = util.ndb_projection(cls=Friend, exclude=['friend_of'])
    qry = Friend.query(Friend.friend_of==user_key).order(Friend.name)
    data = yield qry.fetch_async(projection=proj)
    raise ndb.Return(data)

@ndb.tasklet
def friend_bills_async(user_key):
    """
    Get bill for each friend using projection query
    """
    proj = util.ndb_projection(cls=FriendBill, exclude=['owner'])
    qry = FriendBill.query(FriendBill.owner==user_key)
    data = yield qry.fetch_async(projection=proj)
    raise ndb.Return(data)

@ndb.tasklet
def my_created_bills(user_key=None, **q_options):
    """
    @param user_key: usually current user key
    @return: all bills, sectioned by paid and unpaid
    """
    # Retrieve all bills
    qry = Bill.query(Bill.owner == user_key).order(-Bill.created_time)
    proj = util.ndb_projection(cls=Bill, exclude=['owner'])
    (bills, friends, friend_bills) = yield (qry.fetch_async(projection=proj),
                                            friends_async(user_key),
                                            friend_bills_async(user_key))
    page = yield bill_page(bills, friends, friend_bills)
    
    # Return
    raise ndb.Return(page)


@ndb.tasklet
def my_requested_bills(user_key=None, **q_options):
    """
    Retrieve all bill requested to current user emails
    """
    
    # Get all emails of current user
    user = user_key.get()
    emails = user.auth_ids
    try:
        emails = emails + user.emails
        emails = unique(emails)
    except:
        pass
    
    # Search if current user is friend of other users
    qry = Friend.query(Friend.email.IN(emails))
    friend_keys = yield qry.fetch_async(keys_only=True)
    
    # Retrieve all bills that has email of current user
    friend_bills = []
    if len(friend_keys) > 0:
        qry = FriendBill.query(FriendBill.friend.IN(friend_keys))
        qry = qry.order(-FriendBill.created_time)
        proj = util.ndb_projection(cls=FriendBill, exclude=['friend'])
        friend_bills = yield qry.fetch_async(projection=proj)
        
        # Retrieve owner detail
        friend_bills = yield util.ndb_to_list_async(friend_bills, detail=['owner', 'bill'])
        
    raise ndb.Return(friend_bills)